package glemipsc.instructions;

import glemipsc.Address;
import glemipsc.Function;
import glemipsc.GLEMipsC;
import glemipsc.Type;
import glemipsc.implementations.Argument;
import glemipsc.implementations.Implementation;

import java.io.Serializable;
import java.util.List;

public class Assignment extends BaseInstruction implements Serializable, Instruction, Expression {
	private VariableRef dest;
	private Expression source;
	private Address addr;
	
	public Assignment(Expression dest, Expression source) {
		this.dest = (VariableRef) dest;
		this.source = source;
		if (!dest.getType().isCompatibleWith(source.getType())) {
			// TODO: IncompatibleTypeException?
			throw new RuntimeException();
		}
	}

	public Assignment(String op, Expression dest, Expression source) {
		this.dest = (VariableRef) dest;
		
		if (op.equals("+="))
			this.source = new Sum(dest, source);
		else if (op.equals("-="))
			this.source = new Sub(dest, source);
		else if (op.equals("*="))
			this.source = new Mul(dest, source);
		else if (op.equals("/="))
			this.source = new Div(dest, source);
		else if (op.equals("%="))
			this.source = new Mod(dest, source);
		else if (op.equals("&="))
			this.source = new BitAnd(dest, source);
		else if (op.equals("^="))
			this.source = new BitXor(dest, source);
		else if (op.equals("|="))
			this.source = new BitOr(dest, source);
		else if (op.equals("<<="))
			this.source = new Shift(dest, source, "<<");
		else if (op.equals(">>="))
			this.source = new Shift(dest, source, ">>");
	}
	
	public Expression getOperand(int index) {
		if (index == 0)
			return dest;
		else if (index == 1)
			return source;
		else
			return null;
	}
	
	public int getNumberOfOperands() {
		return 2;
	}

	public int getSize() {
		return 0;
	}

	public Type getType() {
		return dest.getType();
	}

	public int getImmediateValue() {
		return 0;
	}

	public boolean isImmediate() {
		return false;
	}

	public Address getStoreAddress() {
		return addr;
	}

	public int getStoredSize() {
		// TODO Auto-generated method stub
		return 0;
	}

	public boolean isStored() {
		return true;
	}

	public void setStoreAddress(Address addr) {
		this.addr = addr;
	}

	public void computeInstruction(Argument result, List<glemipsc.implementations.Instruction> instructions, Function func) {
		Implementation impl = GLEMipsC.compiler.getEnvironment().implementation;
		// TODO: Calculate variable address with indexes if necessary
		
		source.computeInstruction(result, instructions, func);
		Argument addr = impl.getFreeRegister();
		Address destAddr = dest.computeAddress(addr, instructions, func);
		instructions.add(impl.getSt(result, destAddr, dest.getVariableSize()));
		impl.freeRegister(addr);
	}

}
